package main

import (
	"bytes"
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"text/template"

	corev1 "k8s.io/api/core/v1"                         // 导入 Kubernetes 核心 API 资源，如 Pod、Service、ConfigMap
	"k8s.io/apimachinery/pkg/api/meta"                  // 用于处理 K8S API 元信息，如资源作用范围
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"       // 包含 K8S 资源的元数据，例如 ObjectMeta
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" // 处理非结构化 K8S 资源
	"k8s.io/apimachinery/pkg/runtime/schema"            // 处理资源的 GroupVersionKind(GVK)
	"k8s.io/apimachinery/pkg/runtime/serializer/yaml"   // YAML 解析器，用于解码 K8S 资源
	"k8s.io/client-go/discovery/cached/memory"          // 缓存式发现客户端，用于动态发现 K8S 资源
	"k8s.io/client-go/dynamic"                          // 动态客户端，用于处理非结构化 K8S 资源
	"k8s.io/client-go/kubernetes"                       // Kubernetes 客户端，用于访问 K8S API
	"k8s.io/client-go/restmapper"                       // 资源映射工具，用于将 GVK 映射到 REST 资源
	"k8s.io/client-go/tools/clientcmd"                  // 处理 KubeConfig 文件
)

// 预定义模板数据
var availableData = map[string]string{
	"Namespace": "my-namespace", // 默认命名空间
}

// 定义模板函数
var availableFunctions = template.FuncMap{
	"GeneratePassword": GeneratePasswordFunc, // 生成密码的示例函数
}

// 生成密码的示例函数
func GeneratePasswordFunc() (string, error) {
	return "password", nil
}

// 读取并解析 YAML 模板文件
func ReadTemplate(filePath string) ([]byte, error) {
	tmpl, err := template.New(filepath.Base(filePath)).Funcs(availableFunctions).ParseFiles(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to parse template: %w", err)
	}

	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, availableData); err != nil {
		return nil, fmt.Errorf("failed to execute template: %w", err)
	}

	return buf.Bytes(), nil
}

// 创建 Kubernetes 客户端，返回 Kubernetes API 客户端和动态客户端
func CreateKubernetesClient() (*kubernetes.Clientset, *dynamic.DynamicClient, *restmapper.DeferredDiscoveryRESTMapper, error) {
	kubeConfigPath := os.ExpandEnv("$HOME/.kube/config") // 读取 KubeConfig
	config, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to build config: %w", err)
	}

	// 创建 K8S 客户端集
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to create clientset: %w", err)
	}
	// 创建动态客户端，适用于非结构化 K8S 资源
	// 在 Kubernetes 中，非结构化（Unstructured）资源 是指 没有严格定义的 Go 结构体（Struct）的 Kubernetes 资源。与 corev1.Pod、corev1.Service 这些结构化资源不同，非结构化资源使用 通用 JSON 结构 来存储和处理数据。
	// pod := &corev1.Pod{} // 只能用于 Pod
	// unstructuredObj := &unstructured.Unstructured{} // 可以用于任意资源
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("failed to create dynamic client: %w", err)
	}
	// 发现客户端，用于动态发现 API 资源
	discoveryClient := clientset.Discovery()
	// 创建缓存式的 API 资源发现客户端，提高查询效率
	cachedMapper := memory.NewMemCacheClient(discoveryClient)
	// 创建延迟发现的 REST 映射工具，映射 GVK 到 REST API 资源
	mapper := restmapper.NewDeferredDiscoveryRESTMapper(cachedMapper)

	return clientset, dynamicClient, mapper, nil
}

// 解析 YAML 内容为 Unstructured 对象
func DecodeYAML(yamlContent []byte) (*unstructured.Unstructured, *schema.GroupVersionKind, error) {
	decoder := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
	runtimeObject, groupVersionKind, err := decoder.Decode(yamlContent, nil, nil)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to decode YAML: %w", err)
	}

	unstructuredObj, ok := runtimeObject.(*unstructured.Unstructured)
	if !ok {
		return nil, nil, fmt.Errorf("decoded object is not Unstructured")
	}

	return unstructuredObj, groupVersionKind, nil
}

// 创建命名空间（如果不存在）
func EnsureNamespaceExists(clientset *kubernetes.Clientset, namespace string) error {
	nsClient := clientset.CoreV1().Namespaces()
	_, err := nsClient.Get(context.TODO(), namespace, metav1.GetOptions{})
	if err == nil {
		// 命名空间已存在
		return nil
	}
	// 命名空间不存在，创建它
	ns := &corev1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: namespace,
		},
	}
	_, err = nsClient.Create(context.TODO(), ns, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("failed to create namespace: %w", err)
	}
	fmt.Printf("Created namespace: %s\n", namespace)
	return nil
}

// 应用资源到 Kubernetes 集群，若存在则更新，若不存在则创建
func ApplyResource(clientset *kubernetes.Clientset, dynamicClient dynamic.Interface, mapper *restmapper.DeferredDiscoveryRESTMapper, unstructuredObj *unstructured.Unstructured) error {
	// 获取资源的 REST 映射关系
	mapping, err := mapper.RESTMapping(unstructuredObj.GroupVersionKind().GroupKind(), unstructuredObj.GroupVersionKind().Version)
	if err != nil {
		return fmt.Errorf("failed to get REST mapping: %w", err)
	}

	var resourceREST dynamic.ResourceInterface
	// 根据资源作用范围确定是命名空间级别还是集群级别
	if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
		// 如果资源是命名空间作用域，确保其命名空间已设置
		if unstructuredObj.GetNamespace() == "" {
			unstructuredObj.SetNamespace(corev1.NamespaceDefault)
		}
		// 确保命名空间存在
		if err := EnsureNamespaceExists(clientset, unstructuredObj.GetNamespace()); err != nil {
			return err
		}
		resourceREST = dynamicClient.Resource(mapping.Resource).Namespace(unstructuredObj.GetNamespace())
	} else {
		// 如果是集群作用域的资源
		resourceREST = dynamicClient.Resource(mapping.Resource)
	}

	ctx := context.Background()
	// 检查资源是否已存在
	existing, err := resourceREST.Get(ctx, unstructuredObj.GetName(), metav1.GetOptions{})
	if err == nil {
		// 资源已存在，执行更新操作
		unstructuredObj.SetResourceVersion(existing.GetResourceVersion())
		_, err = resourceREST.Update(ctx, unstructuredObj, metav1.UpdateOptions{})
		if err != nil {
			return fmt.Errorf("failed to update resource: %w", err)
		}
		fmt.Printf("Updated resource: %s/%s\n", unstructuredObj.GetKind(), unstructuredObj.GetName())
	} else {
		// 资源不存在，执行创建操作
		_, err = resourceREST.Create(ctx, unstructuredObj, metav1.CreateOptions{})
		if err != nil {
			return fmt.Errorf("failed to create resource: %w", err)
		}
		fmt.Printf("Created resource: %s/%s\n", unstructuredObj.GetKind(), unstructuredObj.GetName())
	}
	return nil
}

// 解析 YAML 并应用 Kubernetes 资源
func main() {
	myFilePath := "configmap.yaml"
	finalYAML, err := ReadTemplate(myFilePath)
	if err != nil {
		log.Fatalf("Failed to read template: %v", err)
	}

	clientset, dynamicClient, mapper, err := CreateKubernetesClient()
	if err != nil {
		log.Fatalf("Failed to create Kubernetes client: %v", err)
	}

	objectsInYAML := bytes.Split(finalYAML, []byte("---"))
	for _, objectInYAML := range objectsInYAML {
		if len(bytes.TrimSpace(objectInYAML)) == 0 {
			continue
		}
		unstructuredObj, _, err := DecodeYAML(objectInYAML)
		if err != nil {
			log.Printf("Failed to decode YAML: %v", err)
			continue
		}
		if err := ApplyResource(clientset, dynamicClient, mapper, unstructuredObj); err != nil {
			log.Printf("Failed to apply resource: %v", err)
		}
	}
}
